package model;
import java.util.HashMap;
import java.util.Vector;

/**
 * The PartsList class contains a Collection of Components and methods to alter the Components of this Collection.
 */
public class PartsList {
	
	private static final String DoubleDefinitionMessage = "Name bereits vorhanden!";
	final private HashMap<String, ComponentCommon> componentsMap;
	
	private HashMap<String, ComponentCommon> getComponentsMap(){
		return this.componentsMap;
	}
	
	/** Factory method for creating a PartsList.
	 * @return a PartsList.
	 */
	public static PartsList create (){
		return new PartsList(new HashMap<String, ComponentCommon>());
	}
	
	/**
	 * Constructor of PartsList.
	 * @param componentsMap is a HashMap with all components this PartsList should contain.
	 */
	private PartsList(final HashMap<String, ComponentCommon> componentsMap){
		this.componentsMap = componentsMap;
	}

	
	/** Adds a new material to the parts list.
	 * @param name The name of the material.
	 * @param price The price of the material.
	 * @throws Exception In case of a double definition.
	 */
	public void createMaterial(final String name, final int price) throws Exception {
		if (this.getComponentsMap().containsKey(name))
			throw new Exception(DoubleDefinitionMessage);
		final Material newMaterial = Material.create(name,price);
		this.getComponentsMap().put(name, newMaterial);
	}

	/** Adds a new product to the parts list.
	 * @param name The name of the product.
	 * @param price The price of the product.
	 * @throws Exception In case of a double definition.
	 */
	public void createProduct(final String name, final int price) throws Exception {
		if (this.getComponentsMap().containsKey(name))
			throw new Exception(DoubleDefinitionMessage);
		final Product newProduct = Product.create(name,price);
		this.getComponentsMap().put(name, newProduct);
	}
	
	/** Adds a new component to the parts list.
	 * @param component The component to add.
	 * @throws Exception In case of a double definition.
	 */
	public void addComponent(final ComponentCommon component) throws Exception{
		if(this.getComponentsMap().containsKey(component.getName())){
			throw new Exception(DoubleDefinitionMessage);
		}
		this.getComponentsMap().put(component.getName(), component);
	}
	
	
	/** Adds a part in the provided quantity to a component.
	 * @param whole The component that receives the new part.
	 * @param part The component to add.
	 * @param amount The quantity of the component to add.
	 * @throws Exception When trying to add a component to a material.
	 */
	public void addPart(final ComponentCommon whole, final ComponentCommon part, final int amount) throws Exception {
		whole.addPart(part,amount);
	}
	/** Provides the amount of materials a component contains.
	 * @param component The component to search through.
	 * @return The amount of materials.
	 */
	public int getPartCount(final ComponentCommon component) {
		return component.getNumberOfMaterials();
	}
	
	public Vector<ComponentCommon> getComponents() {
		return new Vector<ComponentCommon>(this.getComponentsMap().values());
	}
	
	/** Provides all direct parts of a component.
	 * @param component The component to search through.
	 * @return The direct parts of the component.
	 */
	public Vector<QuantifiedComponent> getParts(final ComponentCommon component) {
		return component.getDirectParts();
	}

	/** Provides all materials of a component.
	 * @param component The component to search through.
	 * @return The materials a component consists of.
	 */
	public Vector<QuantifiedComponent> getMaterialList(final ComponentCommon component) {
		return new Vector<QuantifiedComponent>(component.getMaterialList().getMaterials().values());
	}

	/** Provides the price to manufacture a component.
	 * @param component The component to calculate the price for.
	 * @return The price of the component.
	 */
	public String getOverallPrice(final ComponentCommon component) {
		final int price = component.getOverallPrice();
		return String.valueOf(price);
	}

	/** Changes the price of a component.
	 *  Does not alter the price of the materials this component requires.
	 * @param component The component whose price should be altered.
	 * @param newPrice The new price of the component.
	 */
	public void changePrice(final ComponentCommon component, final int newPrice) {
		component.setPrice(newPrice);
	}
}
